package com.soso.base.util.html;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.mozilla.intl.chardet.nsDetector;
import org.mozilla.intl.chardet.nsPSMDetector;

import com.soso.base.util.other.URLUtil;

/**
 * HTTP请求对象
 * 
 * @author YaoMing
 */
public class HttpRequester {

	/**
	 * GAE
	 * By default, the deadline for a fetch is 5 seconds.
	 * The maximum deadline is 10 seconds.
	 */
	private static Integer readTimeout=60000;// milliseconds
	private static final String DEFAULT_CHARSET="UTF-8";
	private static final Integer TIMEOUT=60000;// GAE限制最大请求延迟60秒
	
	public static HttpResponse sendGet(final String urlString) throws IOException {
		return send(urlString, "GET", null, DEFAULT_CHARSET, DEFAULT_CHARSET, TIMEOUT, null, null);
	}
	public static HttpResponse sendGet(final String urlString, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout) throws IOException {
		return send(urlString, "GET", dataLen, receiveEncoding, defaultContentEncoding, connectTimeout, null, null);
	}

	/**
	 * 发送GET请求
	 * 
	 * @param urlString
	 *            URL地址
	 * @param params
	 *            参数集合
	 * @return 响应对象
	 * @throws IOException
	 */
	public static HttpResponse sendGet(String urlString, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout, Map<String, String> params)
			throws IOException {
		return send(urlString, "GET", dataLen, receiveEncoding, defaultContentEncoding, connectTimeout, params, null);
	}

	/**
	 * 发送GET请求
	 * 
	 * @param urlString
	 *            URL地址
	 * @param params
	 *            参数集合
	 * @param propertys
	 *            请求属性
	 * @return 响应对象
	 * @throws IOException
	 */
	public static HttpResponse sendGet(String urlString, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout, Map<String, String> params,
			Map<String, String> propertys)throws IOException {
		return send(urlString, "GET", dataLen, receiveEncoding, defaultContentEncoding, connectTimeout, params, propertys);
	}

	public static HttpResponse sendPost(final String urlString) throws IOException {
		return send(urlString, "POST", null, DEFAULT_CHARSET, DEFAULT_CHARSET, TIMEOUT, null, null);
	}
	/**
	 * 发送POST请求
	 * 
	 * @param urlString
	 *            URL地址
	 * @return 响应对象
	 * @throws IOException
	 */
	public static HttpResponse sendPost(String urlString, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout) throws IOException {
		return send(urlString, "POST", dataLen, receiveEncoding, defaultContentEncoding, connectTimeout, null, null);
	}

	/**
	 * 发送POST请求
	 * 
	 * @param urlString
	 *            URL地址
	 * @param params
	 *            参数集合
	 * @return 响应对象
	 * @throws IOException
	 */
	public static HttpResponse sendPost(String urlString, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout, Map<String, String> params)
			throws IOException {
		return send(urlString, "POST", dataLen, receiveEncoding, defaultContentEncoding, connectTimeout, params, null);
	}

	/**
	 * 发送POST请求
	 * 
	 * @param urlString
	 *            URL地址
	 * @param params
	 *            参数集合
	 * @param propertys
	 *            请求属性
	 * @return 响应对象
	 * @throws IOException
	 */
	public static HttpResponse sendPost(String urlString, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout, Map<String, String> params,
			Map<String, String> propertys)throws IOException {
		return send(urlString, "POST", dataLen, receiveEncoding, defaultContentEncoding, connectTimeout, params, propertys);
	}

	/**
	 * 发送HTTP请求
	 * 
	 * @param urlString
	 * @return 响映对象
	 * @throws IOException
	 */
	private static HttpResponse send(String urlString, String method, Integer dataLen,String receiveEncoding,String defaultContentEncoding,Integer connectTimeout,
			Map<String, String> parameters, Map<String,String> propertys) throws IOException {
		
		if(defaultContentEncoding==null||"".equals(defaultContentEncoding)){
			defaultContentEncoding=DEFAULT_CHARSET;
		}
		if(connectTimeout==null){
			connectTimeout=TIMEOUT;
		}
		if(parameters==null){
			parameters=new HashMap<String,String>();
		}
		urlString=URLUtil.getUrlParams(parameters,urlString);
		String  param = "";
		for (String key : parameters.keySet()) {
			if(!"".equals(param)){
				param+="&";
			}
			param+=key;
			param+="=";
			if(parameters.get(key)!=null){
				param+=URLEncoder.encode(parameters.get(key),defaultContentEncoding);
			}
		}
		// 如果请求为GET方法，并且参数不为空
		if (method.equalsIgnoreCase("GET") && !"".equals(param) ) {
			if(urlString.indexOf("?")==-1)
				urlString +="?";
			else
				urlString +="&";
			urlString += param;
		}
		// NEW一个URL对象，由该对象的openConnection()方法将生成一个URLConnection对象
		URL url = new URL(urlString);
		HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
		
		// 设置相关属性，具体含义请查阅JDK文档
		urlConnection.setDoOutput(true);
		urlConnection.setDoInput(true);
		urlConnection.setRequestMethod(method);
		urlConnection.setDefaultUseCaches(false);
		urlConnection.setUseCaches(false);
		urlConnection.setConnectTimeout(connectTimeout);
		urlConnection.setReadTimeout(readTimeout);
		
		// 赋予请求属性
		if (propertys != null){
			for (String key : propertys.keySet()) {
				urlConnection.setRequestProperty(key, propertys.get(key));
			}
		}
		// 如果请求为POST方法，并且参数不为空
		if (method.equalsIgnoreCase("POST") && param != null) {
			// 将参数信息发送到HTTP服务器
			// 要注意：一旦使用了urlConnection.getOutputStream().write()方法，urlConnection.setRequestMethod("GET");将失效，其请求方法会自动转为POST
			urlConnection.getOutputStream().write(param.toString().getBytes());
			urlConnection.getOutputStream().flush();
			urlConnection.getOutputStream().close();
		}
		HttpResponse hr=null;
		try {
			hr= makeContent(urlString, dataLen,receiveEncoding, urlConnection);
		} catch (IOException e) {
			throw e;
		} finally {
			// 最终关闭流
			if (urlConnection != null)
				urlConnection.disconnect();
		}
		
		return hr;
	}
	private static void readByByte(InputStream inputStream, Integer dataLen,String receiveEncoding,HttpResponse httpResponser) throws IOException{
		if(receiveEncoding==null||"".equals(receiveEncoding.trim())){
			receiveEncoding=htmlCharsetDetector(inputStream);
		}
		
		if(dataLen==null||dataLen<1){
			dataLen=inputStream.available();
		}
		if(dataLen==null||dataLen<1){
			String content=readByBufferedReader(inputStream,receiveEncoding);
			httpResponser.setContent(content);
			if(content!=null){
				httpResponser.setLength(content.length());
			}
		}
		int bufferLen=1024*5;
		if(bufferLen>dataLen){
			bufferLen=dataLen;
		}
		byte[] htmlByte = new byte[dataLen]; 
		int offset=0;
		int len=-1;
		do{
			len = inputStream.read(htmlByte,offset,bufferLen);
			offset+=len;
			int temp=dataLen-offset;
			if(temp<1){
				break;
			}else if(temp<bufferLen){
				bufferLen=temp;
			}
		}while( len!= -1 );
		httpResponser.setLength(dataLen);
		httpResponser.setContent(new String(htmlByte,receiveEncoding));
	}
	
	private static String readByBufferedReader(InputStream in,String receiveEncoding) throws IOException{
		BufferedReader bufferedReader = new BufferedReader(
				new InputStreamReader(in,receiveEncoding));
		StringBuffer temp = new StringBuffer();
		String line = bufferedReader.readLine();
		while (line != null) {
			temp.append(line);
			line = bufferedReader.readLine();
			if(line != null)
				temp.append("\r\n");
		}
		bufferedReader.close();
        return temp.toString();
	}
	
	public static String htmlCharsetDetector(String urlString) throws IOException {
		URL url = new URL(urlString);
		HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
		urlConnection.setConnectTimeout(TIMEOUT);
		urlConnection.setReadTimeout(readTimeout);
		return htmlCharsetDetector(urlConnection.getInputStream());
	}
	private static String htmlCharsetDetector(InputStream inputStream) throws IOException {
		nsDetector det = new nsDetector(nsPSMDetector.ALL);
		BufferedInputStream imp = new BufferedInputStream(inputStream);
		byte[] buf = new byte[128];
		int len;
		boolean done = false;
		boolean isAscii = true;
		while ((len = imp.read(buf, 0, buf.length)) != -1) {
			if (isAscii)
				isAscii = det.isAscii(buf, len);
			if (!isAscii && !done){
				done = det.DoIt(buf, len, false);
				break;
			}
		}
		det.DataEnd();
		if(det.getProbableCharsets()!=null&&det.getProbableCharsets().length>0){
			return det.getProbableCharsets()[0];
		}
		return DEFAULT_CHARSET;
	}
	
	/**
	 * 得到响应对象
	 * 
	 * @param urlConnection
	 * @return 响应对象
	 * @throws IOException
	 */
	private static HttpResponse makeContent(String urlString, Integer dataLen,String receiveEncoding,
			HttpURLConnection urlConnection) throws IOException {
		HttpResponse httpResponser = new HttpResponse();
		// 得到响应流
		InputStream in = urlConnection.getInputStream();
		readByByte(in,dataLen,receiveEncoding,httpResponser);
		in.close();
		// 得到请求连接的字符集
		String ecoding = urlConnection.getContentEncoding();
		// 将各属性赋值给响应对象
		httpResponser.setUrlString(urlString) ;
		httpResponser.setPort( urlConnection.getURL().getDefaultPort());
		httpResponser.setFile(urlConnection.getURL().getFile());
		httpResponser.setHost(urlConnection.getURL().getHost());
		httpResponser.setPath(urlConnection.getURL().getPath());
		httpResponser.setPort(urlConnection.getURL().getPort());
		httpResponser.setProtocol(urlConnection.getURL().getProtocol());
		httpResponser.setQuery(urlConnection.getURL().getQuery());
		httpResponser.setRef(urlConnection.getURL().getRef());
		httpResponser.setUserInfo(urlConnection.getURL().getUserInfo());
		httpResponser.setContentEncoding(ecoding);
		httpResponser.setCode(urlConnection.getResponseCode());
		httpResponser.setMessage(urlConnection.getResponseMessage());
		httpResponser.setContentType(urlConnection.getContentType());
		httpResponser.setMethod(urlConnection.getRequestMethod());
		httpResponser.setConnectTimeout(urlConnection.getConnectTimeout());
		httpResponser.setReadTimeout(urlConnection.getReadTimeout());
		httpResponser.setCookies(readCookie(urlConnection));
		return httpResponser;
	}
	private static HashMap<String,String> readCookie(HttpURLConnection urlConnection){
		Map<String,List<String>> map=urlConnection.getHeaderFields();
		HashMap<String,String> cookiesMap=new HashMap<String, String>();
		if(map!=null&&map.containsKey("Set-Cookie")){
			List<String> list=(List<String>)map.get("Set-Cookie");
			for(Iterator<String> it=list.iterator();it.hasNext();){
				String[] tempField=it.next().split(";");
				for(String cookie:tempField){
					String[] propertys=cookie.split("=", 2);
					if(!cookiesMap.containsKey(propertys[0])){
						cookiesMap.put(propertys[0], propertys[1]);
					}
				}
			}
		}
		return cookiesMap;
	}


	
}
